package com.fyales.android.util;

import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串工具类，用来判断是否为合法的字符串(邮箱,密码，是否为空等)
 * @author fyales
 * @since 15/7/17.
 */
public class TextUtil {
    public static final String SEPARATOR1 = "-";
    public static final String SEPARATOR2 = ".";
    public static final String SEPARATOR3 = "_";

    private static final String REGULAR_INTEGER = "^[-\\+]?[\\d]+$";
    private static final String REGULAR_DOUBLE = "^([-\\+]?[\\d]+[.]?[\\d]*)|([-\\+]?[\\d]*[.]?[\\d]+)$";
    private static final String REGULAR_DATE = "^[\\d]*[-](([0]?[1-9])|([1][0-2]))[-](([3][0-1])|(([1-2])?[0-9])|([0]?[1-9]))[ ](([0-1]?[0-9])|([2][0-3]))([:]([0-5]?[0-9])){2}$";
    private static final String REGULAR_EMAIL = "^[\\w][\\w-]*([.][\\w-]+){0,3}@[\\w-]+([.][\\w-]+){0,1}([.][\\w]{2,3}){1,3}$";
    private static final String REGULAR_PASSWORD = "^[0-9a-zA-Z]{6,16}$";
    private static final String REGULAR_USERNAME = "^[\\w-]{5,15}$";

    /** 简易年月正则 */
    private static final String REGULAR_YYYY_MM = "^[0-9]{4}[-][0-9]{2}$";
    /** 简易年月日正则 */
    private static final String REGULAR_YYYY_MM_DD = "^[0-9]{4}[-][0-9]{2}[-][0-9]{2}$";

    private static final String REGULAR_DOUBLE_ELEMENT = "[^0-9.]";
    private static final String REGULAR_PUNCTUATION1 = "[;；]";

    private static final String REGULAR_CHAR_NUM = "^[\\da-zA-Z]{6,16}$";

    /**
     * Determine whether it is a valid string
     *
     * @param value
     * @return {@code true} if not {@code null} and the value's length is over 0
     *         (not include the blank), or {@code false}
     */
    public static boolean isValidValue(String value) {
        return value != null && value.trim().length() > 0;
    }

    public static String formatString(String target, String defaultValue) {
        return isValidValue(target) ? target.trim() : defaultValue;
    }

    public static String formatIntString(String target, String defaultValue) {
        if (!isValidValue(target))
            return defaultValue;

        try {
            Integer.parseInt(target.trim());
        } catch (NumberFormatException e) {
            e.printStackTrace();
            return defaultValue;
        }

        return target.trim();
    }

    /**
     * Determine whether it is a valid charSequence
     *
     * @param value
     * @return {@code true} if not {@code null} and the value's length is over 0
     *         (not include the blank), or {@code false}
     */
    public static boolean isValidText(CharSequence value) {
        return value != null && value.toString().trim().length() > 0;
    }

    /**
     * 格式化输出字符串
     *
     * @param cs 待格式化的字符串
     * @param def 切记不要传入空字符串
     * @return 返回格式化后的字符
     */
    public static String formatString(CharSequence cs, String def) {
        if (isValidText(cs))
            return cs.toString().trim();

        return def;
    }

    /**
     * Determine whether it is a {@code float} or a {@code double}
     * <p>
     * <table border="2" width="85%" align="center" cellpadding="5">
     * <thead>
     * <tr>
     * <th>String</th>
     * <th>Result</th>
     * </tr>
     * </thead> <tbody>
     * <tr>
     * <td rowspan="1">"."</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">"0."</td>
     * <td>{@code true}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">".0"</td>
     * <td>{@code true}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">"6e3"</td>
     * <td>{@code false}; if need to return true, this method is need to be
     * modified</td>
     * </tr>
     * </tbody>
     * </table>
     * </p>
     * <p>
     * Do not consider the synchronize and unsynchronized thread, so the
     * performance needs to be paid attentions by yourself
     * </p>
     *
     * @param str string to be determined
     * @return {@code true} if <var>str</var> is a {@code float} or a
     *         {@code double}, or {@code false}
     */
    public static boolean isDouble(String str) {
        if (str == null)
            return false;

        try {
            Double.parseDouble(str);
        } catch (NumberFormatException e) {
            return false;
        }

        return true;

        // Pattern pattern = Pattern.compile(REGULAR_DOUBLE);
        // return pattern.matcher(str).matches();
    }

    /**
     * Determine whether it is a {@code integer}
     * <p>
     * <li>Do not consider the synchronize and unsynchronized thread, so the
     * performance needs to be paid attentions by yourself</li>
     * <li>The String "\u002D" is not supported, however, it could be parsed by
     * the method {@link Integer#parseInt(String)}</li>
     * <li><strong>Only support simple integer format</strong></li>
     * </p>
     *
     * @param str String to be determined
     * @return {@code true} if <var>str</var> is a {@code integer}, or
     *         {@code false}
     */
    public static boolean isInteger(String str) {
        if (str == null)
            return false;

        Pattern pattern = Pattern.compile(REGULAR_INTEGER);
        return pattern.matcher(str).matches();
    }

    /**
     * Determine whether it is valid date
     * <p>
     * <li>Do not distinguish the Average year and the Leap year, so the
     * "2012-02-31 12:12:12" is right</li>
     * <li>Do not distinguish the Otsuki and the Satsuki, so the
     * "2012-04-31 12:12:12" is right</li>
     * <li>Do not consider the synchronize and unsynchronized thread, so the
     * performance needs to be paid attentions by yourself</li>
     * </p>
     * <p>
     * <table border="2" width="85%" align="center" cellpadding="5">
     * <thead>
     * <tr>
     * <th>Example</th>
     * <th>Result</th>
     * </tr>
     * </thead> <tbody>
     * <tr>
     * <td rowspan="1">"002014-4-31 23:59:59"</td>
     * <td>{@code true}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">"000-02-31 0:0:0"</td>
     * <td>{@code true}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">"2013-03-06 24:00:00"</td>
     * <td>{@code false}</td>
     * </tr>
     * </tbody>
     * <tr>
     * <td rowspan="1">"2013-03-06 23:60:00"</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">"2013-03-06 23:00:60"</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">"2013-003-006 23:00:00"</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">"9999-12-31 23:59:59"</td>
     * <td>{@code true}</td>
     * </tr>
     * </table>
     * </p>
     *
     * @param strDate
     * @return {@code true} <var>strDate</var> is valid data, or {@code false}
     */
    public static boolean isValidDate(String strDate) {
        if (!isValidValue(strDate))
            return false;

        Pattern pattern = Pattern.compile(REGULAR_DATE);
        return pattern.matcher(strDate).matches();
    }

    /**
     * Parse the Cid String "a-b"
     * <p>
     * <li>a is an integer, b is an integer</li>
     * <li>a is the cityId, b is the proId</li>
     * </p>
     *
     * @param cid such as "11-121432131"
     * @return new int[2], e.g. int[]{11,121432131}
     */
    public static int[] getCid(String cid) {
        return parseInt(cid, SEPARATOR1);
    }

    /**
     * Parse the idRegionbolck String "a-b"
     * <p>
     * <li>a is an integer, b is an integer</li>
     * <li>a is the regionId, b is the blockId</li>
     * </p>
     *
     * @param idRegionbolck such as "1-2"
     * @return new int[2], e.g. int[]{1,2}
     */
    public static int[] getRegBloId(String idRegionbolck) {
        return parseInt(idRegionbolck, SEPARATOR1);
    }

    /**
     * Verify the email
     * <p>
     * <table border="1" width="85%" align="center" cellpadding="5">
     * <thead>
     * <tr>
     * <th>Example</th>
     * <th>Result</th>
     * </tr>
     * </thead> <tbody>
     * <tr>
     * <td rowspan="1">""</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">null</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">abc.def@sina.com.cn</td>
     * <td>{@code true}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">abc.def@sina.flags.com.cn</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">.def@sina.com.cn</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">abc@.com.cn</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">abc@126.com</td>
     * <td>{@code true}</td>
     * </tr>
     * </tbody>
     * </table>
     * </p>
     *
     * @param email
     * @return
     */
    public static boolean isValidEmail(String email) {
        if (!isValidValue(email))
            return false;

        Pattern pattern = Pattern.compile(REGULAR_EMAIL);
        return pattern.matcher(email).matches();
    }

    public static RegularFeedBack isRegexEmail(String email) {
        if (!TextUtil.isValidValue(email))
            return new RegularFeedBack(false, "邮箱不能为空");

        if (email.length() < 6 || email.length() > 40)
            return new RegularFeedBack(false, "邮箱长度6-40个字符，请重新输入");

        if (email.startsWith("-"))
            return new RegularFeedBack(false, "邮箱不能以\"-\"开头");

        if (!TextUtil.isValidEmail(email))
            return new RegularFeedBack(false, "邮箱格式有误或含有非法字符");

        return new RegularFeedBack(true, "");
    }

    /**
     * Verify the password
     * <p>
     * <li>Do not contain blank</li>
     * <li>Only contain the character such as "A-Z","a-z","-","_","0-9"</li>
     * <li>Length:6-16</li>
     * </p>
     * <p>
     * <table border="1" width="85%" align="center" cellpadding="5">
     * <thead>
     * <tr>
     * <th>Example</th>
     * <th>Result</th>
     * </tr>
     * </thead> <tbody>
     * <tr>
     * <td rowspan="1">""</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">null</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">abcdef</td>
     * <td>{@code true}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">a bcdef</td>
     * <td>{@code false}</td>
     * </tr>
     * </tbody>
     * </table>
     * </p>
     *
     * @param password
     * @return
     */
    public static boolean isValidPassword(String password) {
        if (!isValidValue(password))
            return false;

        Pattern pattern = Pattern.compile(REGULAR_PASSWORD);
        return pattern.matcher(password).matches();
    }

    /** 微聊用密码正则验证 */
    public static boolean isValidChatPsd(String password) {
        if (!isValidValue(password))
            return false;

        Pattern pattern = Pattern.compile(REGULAR_CHAR_NUM);
        return pattern.matcher(password).matches();

    }

    /**
     * Verify the password <h3>More Detail</h3>
     * <ul>
     * <li>The valid char of the password: >=6 and <=16</li>
     * <li>Not null or some blank</li>
     * <li>Only contains the numbers and characters, such as A-Za-z0-9</li>
     * <li>Do not support the chars, such as _-!@#$%^*()`~</li>
     * </ul>
     *
     * @see #isValidPassword(String)
     * @param password
     * @return
     */
    public static RegularFeedBack isRegexPassword(String password, String confirmPassword) {
        if (!isValidValue(password))
            return new RegularFeedBack(false, "Password can't be empty");

        if (!password.equals(confirmPassword))
            return new RegularFeedBack(false, "Passwords don't match");

        if (password.length() < 6 || password.length() > 16)
            return new RegularFeedBack(false, "The length length is between six and sixteen");

        if (!isValidPassword(password))
            return new RegularFeedBack(false, "Illegal character");

        return new RegularFeedBack(true, "");
    }


    public static RegularFeedBack isRegexPassword(String oldPassword, String newPassword, String confirmPassword) {
        if (!isValidValue(oldPassword) || !isValidValue(newPassword))
            return new RegularFeedBack(false, "密码不能为空");

        if (!newPassword.equals(confirmPassword))
            return new RegularFeedBack(false, "两次密码输入不一致");

        if (oldPassword.length() < 6 || oldPassword.length() > 16)
            return new RegularFeedBack(false, "原密码有效字符数应在6-16个");

        if (newPassword.length() < 6 || newPassword.length() > 16)
            return new RegularFeedBack(false, "新密码有效字符数应在6-16个");

        if (!isValidPassword(oldPassword) || !isValidPassword(newPassword))
            return new RegularFeedBack(false, "密码含有非法字符");

        return new RegularFeedBack(true, "");
    }

    /**
     * Verify the password
     * <p>
     * <li>Do not contain blank</li>
     * <li>Only contain the character such as "A-Z","a-z","-","_","0-9"</li>
     * <li>Length:5-15</li>
     * </p>
     * <p>
     * <table border="1" width="85%" align="center" cellpadding="5">
     * <thead>
     * <tr>
     * <th>Example</th>
     * <th>Result</th>
     * </tr>
     * </thead> <tbody>
     * <tr>
     * <td rowspan="1">""</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">null</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">abcde</td>
     * <td>{@code true}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">abcdefghijklmno</td>
     * <td>{@code false}</td>
     * </tr>
     * <tr>
     * <td rowspan="1">a bcdef</td>
     * <td>{@code false}</td>
     * </tr>
     * </tbody>
     * </table>
     * </p>
     *
     * @param username
     * @return
     */
    public static boolean isValidUserName(String username) {
        if (!isValidValue(username))
            return false;

        Pattern pattern = Pattern.compile(REGULAR_USERNAME);
        return pattern.matcher(username).matches();
    }

    /**
     * Parse the string with the given <var>separator</var>, and get the
     * int-array
     * <p>
     * <li>Base formatter string, e.g. "a-b","a.b","a_b"</li>
     * <li>Ensure the "a" has not the separator such as "-", ".", "_", and so on
     * </li>
     * <li>The "a" is an int, and the "b" is an int</li>
     * </p>
     *
     * @param strs
     * @param separator
     * @return
     */
    private static int[] parseInt(String strs, String separator) {
        if (separator == null)
            throw new NullPointerException("the seperator is not null");

        if (!TextUtil.isValidValue(strs))
            return null;

        String[] cidPair = strs.split(separator);
        if (cidPair == null || cidPair.length <= 1)
            return null;

        if (!TextUtil.isInteger(cidPair[0]) || !TextUtil.isInteger(cidPair[1]))
            return null;

        return new int[] {
                Integer.parseInt(cidPair[0]), Integer.parseInt(cidPair[1])
        };
    }

    /**
     * Contents, see {@link #compareNumStrings(String, String)} please.
     *
     * @param d1 the first {@code double} to be compared with another.
     * @param d2 the second {@code double} to be compared with another.
     * @return {@code 1} if {@code d1 > d2}, {@code -1} if {@code d1 < d2},
     *         {@code 0} if {@code d1 == d2}
     */
    public static int compareInDoubles(double d1, double d2) {
        return compareNumStrings(Double.toString(d1), Double.toString(d2));
    }

    /**
     * Contents, see {@link #compareNumStrings(String, String)} please.
     *
     * @param f1 the first {@code float} to be compared with another.
     * @param f2 the second {@code float} to be compared with another.
     * @return {@code 1} if {@code f1 > f2}, {@code -1} if {@code f1 < f2},
     *         {@code 0} if {@code f1 == f2}
     */
    public static int compareInFloats(float f1, float f2) {
        return compareNumStrings(Float.toString(f1), Float.toString(f2));
    }

    /**
     * Contents, see {@link #compareNumStrings(String, String)} please.
     *
     * @param f the {@code float} to be compared with another.
     * @param d the {@code double} to be compared with another.
     * @return {@code 1} if {@code f > d}, {@code -1} if {@code f < d},
     *         {@code 0} if {@code f == d}
     */
    public static int compareDoubleFloat(float f, double d) {
        return compareNumStrings(Float.toString(f), Double.toString(d));
    }


    public static int compareDoubleString(double d, String s) {
        return compareNumStrings(Double.toString(d), s);
    }

    /**
     * Contents, see {@link #compareNumStrings(String, String)} please.
     * <p>
     * 该方法暂时只能用来判断是否为0，负数判断有误
     *
     * @param f the {@code float} to be compared with another.
     * @param s the {@code string} to be compared with another.
     * @return {@code 1} if {@code f > s}, {@code -1} if {@code f < s},
     *         {@code 0} if {@code f == s}, {@code -2} if {@code s} does not
     *         contain a valid string representation of a big decimal.
     */
    public static int compareFloatString(float f, String s) {
        return compareNumStrings(Float.toString(f), s);
    }

    /**
     * Compares the {@code String}s. Returns one of the four values {@code -2},
     * {@code 1}, {@code 0}, or {@code -1}.If this difference is > 0 then 1 is
     * returned, if the difference is < 0 then -1 is returned, and if the
     * difference is 0 then 0 is returned, if one of the string falls to parse
     * then -2 is returned. This means, that if two strings instances are
     * compared which are equal in value but differ in scale, then these two
     * instances are considered as equal.
     *
     * @param s1 the first value to be compared with another.
     * @param s2 the second value to be compared with another.
     * @return {@code 1} if numbers of {@code s1 > s2}, {@code -1} if numbers of
     *         {@code s1 < s2}, {@code 0} if numbers of {@code s1 == s2},
     *         {@code -2} if {@code s1 and s2} does not contain a valid string
     *         representation of a big decimal.
     */
    public static int compareNumStrings(String s1, String s2) {
        BigDecimal bg1, bg2;
        try {
            bg1 = new BigDecimal(s1);
            bg2 = new BigDecimal(s2);
        } catch (NumberFormatException e) {
            return -2;
        }

        return bg1.compareTo(bg2);
    }

    public static class RegularFeedBack {
        private boolean isValid;
        private String error;

        public RegularFeedBack(boolean isValid, String error) {
            this.isValid = isValid;
            this.error = error;
        }

        public boolean isValid() {
            return isValid;
        }

        public void setValid(boolean isValid) {
            this.isValid = isValid;
        }

        public String getError() {
            return error;
        }

        public void setError(String error) {
            this.error = error;
        }

    }

    /**
     * 截取标准日期时间字符串"yyyy-MM-dd HH:mm:ss"，去掉秒
     *
     * @param datetime
     * @return
     */
    public static String subStringDateTimess(String datetime) {
        if (datetime == null)
            return "";

        if (!isValidValue(datetime))
            return "";

        int lastIndexColon = datetime.lastIndexOf(":");

        if (lastIndexColon > 0)
            return datetime.substring(0, lastIndexColon);
        else
            return datetime;
    }

    /**
     * 解析String为integer
     * <p>
     * 当整形数据过大会导致解析失败，需使用{@link Long#parseLong(String)}进行解析
     * </p>
     *
     * @param str 需要确保你解析的数据范围维持在Int范围
     * @param defaultValue 默认返回数值，如解析失败
     * @return
     */
    public static int parseIntStr(String str, int defaultValue) {
        if (!TextUtil.isValidValue(str))
            return defaultValue;

        try {
            return Integer.parseInt(str);
        } catch (NumberFormatException e) {
            e.printStackTrace();
            return defaultValue;
        }
    }

    public static long parseLongStr(String str, long defaultValue) {
        if (!TextUtil.isValidValue(str))
            return defaultValue;

        try {
            return Long.parseLong(str);
        } catch (NumberFormatException e) {
            e.printStackTrace();
            return defaultValue;
        }
    }

    /**
     * 判断字符串是否是数字
     *
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        return isNum.matches();
    }

    public static boolean isDate_YYYY_MM(String yyyymm) {
        if (!isValidValue(yyyymm))
            return false;
        Pattern pattern = Pattern.compile(REGULAR_YYYY_MM);
        Matcher matcher = pattern.matcher(yyyymm);
        return matcher.matches();
    }

    public static boolean isCharNum(String str) {
        if (!isValidValue(str))
            return false;
        Pattern pattern = Pattern.compile(REGULAR_CHAR_NUM);
        Matcher matcher = pattern.matcher(str);
        return matcher.matches();
    }

    public static boolean isDate_YYYY_MM_DD(String yyyymmdd) {
        if (!isValidValue(yyyymmdd))
            return false;
        Pattern pattern = Pattern.compile(REGULAR_YYYY_MM_DD);
        Matcher matcher = pattern.matcher(yyyymmdd);
        return matcher.matches();
    }

    /**
     * 取出字符串中的数字和小数点
     *
     * @param pm
     * @return
     */
    public static String extractDouble(String pm) {
        if (!isValidValue(pm))
            return "";

        Pattern pattern = Pattern.compile(REGULAR_DOUBLE_ELEMENT);
        Matcher matcher = pattern.matcher(pm);
        return matcher.replaceAll("").trim();
    }

    /**
     * 格式化字符串
     * <p>
     * 功能：把字符串中的分号 ";""；" 替换为 顿号 ”，“
     *
     * @param str
     * @return
     */
    public static String formatTextReplaceSemicolon(String str) {
        if (!isValidText(str))
            return "";

        Pattern pattern = Pattern.compile(REGULAR_PUNCTUATION1);
        Matcher matcher = pattern.matcher(str);
        return matcher.replaceAll("，").trim();
    }

    public static String formatCityname(String cityname) {
        if (!isValidValue(cityname))
            return "";

        String citynametrimed = cityname.trim();
        if (citynametrimed.equals("市"))
            return "";

        if (!citynametrimed.endsWith("市"))
            return citynametrimed;

        return citynametrimed.substring(0, citynametrimed.length() - 1);
    }
}
